<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="root">
        <h3 id="hMyname">{myname}</h3>
        <p id="pMyname">{myname}</p>
        <h3 id="hMyage">{myage}</h3>
        <p id="pMyage">{myage}</p>
        <h3 id="hInfo">{myname}-{myage}</h3>
        <p id="pInfo">{myname}-{myage}</p>
    </div>

    <script>
        function update(elementListener) {
            let [elem, elemTemplate] = elementListener
            elemTemplate.match(reg)
            elemTemplate.match(reg).forEach(
                placeholder => elemTemplate = elemTemplate.replace(placeholder, data[placeholder.slice(1, -1)])
            )
            elem.innerHTML = elemTemplate
        }

        function registerProperties(obj, properties) {
            properties.forEach(
                p => {
                    console.log("p", p);
                    obj.propListeners = obj.propListeners || {}

                    obj.propListeners[p.name] = new Set()
                    p.listeners.forEach(
                        lis => obj.propListeners[p.name].add(lis)
                    )

                    Object.defineProperty(obj, p.name, {
                        get() {
                            console.log("getter");
                            return obj["stored_" + p.name]
                        },
                        set(newValue) {
                            console.log("setter", newValue);
                            obj["stored_" + p.name] = newValue

                            p.listeners.forEach(
                                lis => {
                                    console.log(lis, "'s innerText has changed to", newValue);
                                    update(lis)
                                }
                            )
                        }
                    })

                }
            )
        }

        class WatcherMap {
            register(prop, elem) {
                this[prop] = this[prop] || new Set()
                this[prop].add([elem, elem.innerText])
            }
        }

        const data = {}
        const reg = /{(\w+)}/g
        const wm = new WatcherMap()

        Array.from(root.children).filter(c => c.nodeName !== "SCRIPT").forEach(
            c => {
                console.log(c.nodeName);
                c.innerHTML.match(reg).map(value => value.slice(1, -1)).forEach(
                    prop => wm.register(prop, c)
                )
            }
        )
        console.log("wm", wm);

    </script>

    <script>
        registerProperties(data, [
            { name: "myname", listeners: wm.myname },
            { name: "myage", listeners: wm.myage },
        ])

        data.myname = "黑哥"
        data.myage = 40

        setTimeout(() => {
            data.myname = "白鸽"
            data.myage = 35
            console.log("data changed!");
        }, 3000);
    </script>
</body>

</html>